package functionroots.methods;

import functionroots.primitives.MaximumIterationsException;
import functionroots.primitives.RealFunction;
import functionroots.primitives.RootFinder;

/**
 * Implementa o método de Newton-Raphson para a obtenção de zeros de funções de
 * uma variável real.
 * @author orion
 */
public class Secant extends RootFinder<SecantData> {

    /**
     * Instancia o método de Newton-Raphson com uma função, precisão e número
     * máximo de iterações pré-definidos.
     * @param function a função alvo.
     * @param precision a precisão pretendida.
     * @param maxIterations o número máximo de iterações.
     */
    public Secant(RealFunction function, double precision, long maxIterations) {
        super(function, precision, maxIterations);
    }

    /**
     * Instancia o método de Newton-Raphson com uma função e precisão
     * pré-definidas.
     * @param function a função alvo.
     * @param precision a precisão pretendida.
     */
    public Secant(RealFunction function, double precision) {
        super(function, precision);
    }

    public double scan(SecantData data) throws MaximumIterationsException {
        double oldX = data.x0;
        double newX = data.x1;
        double oldY = getFunction().value(oldX);
        double newY = getFunction().value(newX);
        double temp;
        if (Math.abs(oldY) < getPrecision()) {
            return oldX;
        } else if (Math.abs(newY) < getPrecision()) {
            return newX;
        } else if (Math.abs(oldX - newX) < getPrecision()) {
            return newX;
        }
        for (int i = 0; i < getMaxIterations(); i++) {
            temp = newX;
            newX = (oldX * newY - newX * oldY) / (newY - oldY);
            oldX = temp;
            oldY = newY;
            newY = getFunction().value(newX);
            if (Math.abs(oldY) < getPrecision()) {
                return oldX;
            } else if (Math.abs(newY) < getPrecision()) {
                return newX;
            } else if (Math.abs(oldX - newX) < getPrecision()) {
                return newX;
            }
        }
        throw new MaximumIterationsException(this, data, newX);
    }
}
